
#define _NO_INCLIST
#include <eltypes.h>
#include <limits.h>
#include <_pubcrt.h>

#include "inc/ucnv.h"
#include "local.h"

PVoid _Impl_Get_Encoding_Data()
{
    UInt32 uSize = 0;
    PVoid pv = NULL;
    ECode ec = CrtGetBootModule(L"encoding.dat", (UInt32 *)&pv, &uSize);
    if (FAILED(ec)) {
        return NULL;
    }

    return pv;
}

Int32 _toWString(const char *encoding, wchar_t *dest, Int32 *pdestSize,
                const char *src, Int32 *psrcSize)
{
    UErrorCode errorCode = U_ZERO_ERROR;//initial ErrorCode to 0
    Int32 ret = -1;
    UConverter *cnv = NULL;

    if ((*psrcSize == 0 && dest) || (*pdestSize == 0 && dest) || src == NULL) {
    	*pdestSize = 0;
    	return 0;
    }

    cnv = ucnv_internal_open(encoding, &errorCode);
    if(U_FAILURE(errorCode)) {
        return -1;
    }

    if (dest == NULL) {
    	*pdestSize = 0;
    	*psrcSize = -1;
	}

    ret = ucnv_toUChars(cnv, dest, pdestSize, src, psrcSize, &errorCode);
    if(U_FAILURE(errorCode) && errorCode != U_BUFFER_OVERFLOW_ERROR) {
    	ret = -1;
    }

    ucv_internal_close(encoding, cnv);

    return ret;
}

Int32 _fromWString(const char *encoding, char *dest, Int32 *pdestSize,
                  const wchar_t *src, Int32 *psrcSize)
{
    UErrorCode errorCode = U_ZERO_ERROR;//initial ErrorCode to 0
    Int32 ret = -1;
    UConverter *cnv = NULL;

    if ((*psrcSize == 0 && dest) || (*pdestSize == 0 && dest) || src == NULL) {
    	*pdestSize = 0;
    	return 0;
    }

    cnv = ucnv_internal_open(encoding, &errorCode);
   	if(U_FAILURE(errorCode)) {
        return -1;
    }

    if (dest == NULL) {
    	*pdestSize = 0;
    	*psrcSize = -1;
    }

    ret = ucnv_fromUChars(cnv, dest, pdestSize, src, psrcSize, &errorCode);
    if(U_FAILURE(errorCode) && errorCode != U_BUFFER_OVERFLOW_ERROR) {
        ret = -1;
   	}

    ucv_internal_close(encoding, cnv);

    return ret;
}

Int32 _getAStringLength(const char *encoding, const char *str)
{
    const char *pEnd = str +  strlen(str) + 1;
    Int32 len = -1, count = -1;
    UChar32 c = 0xFFFFFFFF;
    UErrorCode errorCode = U_ZERO_ERROR;

    if (!encoding) encoding = ucnv_getDefaultName();

    if (encoding && !_stricmp(encoding, "UTF-8")) {
        while (str <= pEnd && count != 0) {
            if (*(UInt8 *)str <= 0x7f) {
                count = 1;
            }
            else if (*(UInt16 *)str <= 0x7ff) {
                count = 2;
            }
            else if (*(UInt16 *)str <= 0xd7ff) {
                count = 3;
            }
            else if (*(UInt16 *)str<=0xdfff || *(UInt32 *)str>0x10ffff) {
                return -1;
            }
//            else if (*(UInt16 *)str<=0xffff) {
//                count = 3;
//            }
            else {
                count = 4;
            }

            str += count;
            len++;
        }
    }
    else {
        UConverter *cnv = ucnv_internal_open(encoding, &errorCode);
        if(U_FAILURE(errorCode)) return -1;

        while (c != 0) {
            c = ucnv_getNextUChar(cnv, (const char **)&str, pEnd, &errorCode);
            if(U_FAILURE(errorCode)) break;
            len++;
        }

        ucv_internal_close(encoding, cnv);
    }

    return len;
}

Int32 _getWStringLength(const wchar_t *str)
{
    Int32 len = -1;
    UChar32 c = 0xFFFFFFFF;

    while (c != 0 ) {
        c = *str;
        str++;
        if (U16_IS_SURROGATE(c)) str++;
        len++;
    }

    return len;
}

Char32 _getAStringCharAt(const char *encoding, const char *str, Int32 index)
{
    Int32 count = 0;
    Char32 ret = 0xFFFFFFFF;
    char *p = (char *)str;
    char *pEnd = p +  strlen(p) + 1;
    UChar32 c = 0xFFFFFFFF;
    UErrorCode errorCode = U_ZERO_ERROR;

    UConverter *cnv = ucnv_internal_open(encoding, &errorCode);
    if(U_FAILURE(errorCode)) return 0xFFFFFFFF;

    while (c != 0 ) {
        c = ucnv_getNextUChar(cnv, (const char **)&p, pEnd, &errorCode);
        if(U_FAILURE(errorCode)) break;

        if (count == index) {
            ret =  (Char32)c;
            break;
        }
        count++;
    }
    ucv_internal_close(encoding, cnv);

    return ret;
}

Char32 _getWStringCharAt(const wchar_t *str, Int32 index)
{
    Int32 count = 0;
    Char32 ret = 0xFFFFFFFF;
    UChar32 c = 0xFFFFFFFF;

    while (c != 0) {
        c = *str++;
        if (U16_IS_SURROGATE(c)) {
            c = U16_GET_SUPPLEMENTARY(c, *str++);
        }
        if (count == index) {
            ret = (Char32)c;
            break;
        }
        count++;
    }

    return ret;
}

Int32 _getAStringIndexOf(const char *encoding, const char *str, Char32 ch)
{
    Int32 count = 0, ret = -1;
    char *p = (char *)str;
    char *pEnd = p +  strlen(p) + 1;
    UChar32 c = 0xFFFFFFFF;
    UErrorCode errorCode = U_ZERO_ERROR;

    UConverter *cnv = ucnv_internal_open(encoding, &errorCode);
    if(U_FAILURE(errorCode)) return -1;

    while (c != 0 ) {
        c = ucnv_getNextUChar(cnv, (const char **)&p, pEnd, &errorCode);
        if(U_FAILURE(errorCode)) break;

        if (c == (UChar32)ch) {
            ret = count;
            break;
        }
        count++;
    }
    ucv_internal_close(encoding, cnv);

    return ret;
}

Int32 _getWStringIndexOf(const wchar_t *str, Char32 ch)
{
    Int32 count = 0, ret = -1;
    UChar32 c = 0xFFFFFFFF;

    while (c != 0) {
        c = *str++;
        if (U16_IS_SURROGATE(c)) {
            c = U16_GET_SUPPLEMENTARY(c, *str++);
        }
        if (c == (UChar32)ch) {
            ret = count;
            break;
        }
        count++;
    }

    return ret;
}

Int32 _getAStringLastIndexOf(const char *encoding, const char *str, Char32 ch)
{
    Int32 count = 0, ret = -1;
    char *p = (char *)str;
    char *pEnd = p +  strlen(p) + 1;
    UChar32 c = 0xFFFFFFFF;
    UErrorCode errorCode = U_ZERO_ERROR;

    UConverter *cnv = ucnv_internal_open(encoding, &errorCode);
    if(U_FAILURE(errorCode)) return -1;

    while (c != 0 ) {
        c = ucnv_getNextUChar(cnv, (const char **)&p, pEnd, &errorCode);
        if(U_FAILURE(errorCode)) break;

        if (c == (UChar32)ch) ret = count;
        count++;
    }
    ucv_internal_close(encoding, cnv);

    return ret;
}

Int32 _getWStringLastIndexOf(const wchar_t *str, Char32 ch)
{
    Int32 count = 0, ret = -1;
    UChar32 c = 0xFFFFFFFF;

    while (c != 0) {
        c = *str++;
        if (U16_IS_SURROGATE(c)) {
            c = U16_GET_SUPPLEMENTARY(c, *str++);
        }
        if (c == (UChar32)ch) ret = count;

        count++;
    }

    return ret;
}

char* _getAStringSubstring(const char *encoding, const char *str, Int32 start,
            Int32 len, char *substr)
{
    Int32 count = 0;
    char *p = (char *)str;
    char *pEnd = p +  strlen(p) + 1;
    char *p1 = p, *p2 = pEnd;
    UChar32 c = 0xFFFFFFFF;
    UErrorCode errorCode = U_ZERO_ERROR;
    char *p3 = p;

    UConverter *cnv = ucnv_internal_open(encoding, &errorCode);
    if(U_FAILURE(errorCode)) return NULL;

    while (c != 0) {
        c = ucnv_getNextUChar(cnv, (const char **)&p, pEnd, &errorCode);
        if(U_FAILURE(errorCode)) break;

        if (count == start) {
            p1 = p3;
            if (len == -1) break;
        }
        if (count == start + len && len != -1) {
            p2 = p3;
            break;
        }
        count++;
        p3 = p;
    }
    ucv_internal_close(encoding, cnv);

    if (substr) {
    	*(substr + (p2 - p1)) = '\0';
        return strncpy(substr, p1, p2 - p1);
    }
    else {
        return p1;
    }
}

wchar_t* _getWStringSubstring(const wchar_t *str, Int32 start,
            Int32 len, wchar_t *substr)
{
    Int32 count = 0;
    UChar32 c = 0xFFFFFFFF;
    const wchar_t *p1 = str, *p2 = str + wcslen(str) + 1;
    const wchar_t *p3 = str;

    while (c != 0 || count != start) {
        c = *str++;
        if (U16_IS_SURROGATE(c)) {
            c = U16_GET_SUPPLEMENTARY(c, *str++);
        }

        if (count == start) {
            p1 = p3;
            if (len == -1) break;
        }
        if (count == start + len && len != -1) {
            p2 = p3;
            break;
        }

        count++;
        p3 = str;
    }

    if (substr) {
    	*(substr + (p2 - p1)) = L'\0';
        return wcsncpy(substr, p1, p2 - p1);
    }
    else {
        return (wchar_t *)p1;
    }
}

/*ARGSUSED*/
size_t __cdecl MbsToWcs(wchar_t *pwcs, const char *s, size_t n)
{
    size_t len, insize, outsize;
    Int32 ret = 0;
    // dest string exists, but 0 bytes to be converted
    if (!s || (pwcs && (n == 0))) return (size_t) 0;

    len = strlen(s) + 1;
    insize = MIN(n, len);
    outsize = insize;
    ret = _toWString(NULL, pwcs, (Int32 *)&outsize, s, (Int32 *)&insize);
    if (pwcs && ret != -1) ret = insize;

    return ret;
}

/*ARGSUSED*/
size_t __cdecl WcsToMbs(char *s, const wchar_t *pwcs, size_t n)
{
    size_t len, insize, outsize;
    Int32 ret = 0;

    if (n > INT_MAX) return -1;

    // dest string exists, but 0 bytes to be converted
    if (!pwcs || (s && (n == 0)))
        return (size_t) 0;

    len = wcslen(pwcs) + 1;
    insize = MIN(n, len);
    outsize = MIN(n, (0x7FFFFFFE - (size_t)s));
    ret = _fromWString(NULL, s, (Int32 *)&outsize, pwcs, (Int32 *)&insize);
    if (s && ret != -1) ret = outsize;
    return ret;
}

//Int32 _Convert(const char *toConverterName, const char *fromConverterName,
//             char *target, int32_t targetCapacity,
//             const char *source, int32_t sourceLength)
//{
//    UErrorCode errorCode = U_ZERO_ERROR;//initial ErrorCode to 0
//    return ucnv_convert(toConverterName, fromConverterName,
//             target, targetCapacity, source, sourceLength, errorCode);
//}
